package cn.zyl.demo.java.sort;

/**
 * QuickSort  类说明: 快速排序
 * ---------------------------------------------------------------------------------------------
 *  <p>快速排序是由东尼·霍尔所发展的一种排序算法。在平均状况下，排序 n 个项目要 Ο(n log n) 次比较。在最坏状况下则需要 Ο(n2) 次比较，
 *  但这种状况并不常见。事实上，快速排序通常明显比其他 Ο(n log n) 算法更快，因为它的内部循环（inner loop）可以在大部分的架构上很有效率地被实现出来。</p>
 *  <p>快速排序使用分治法（Divide and conquer）策略来把一个串行（list）分为两个子串行（sub-lists）</p>
 *  <p>快速排序又是一种分而治之思想在排序算法上的典型应用。本质上来看，快速排序应该算是在冒泡排序基础上的递归分治法。</p>
 *  <p>快速排序的名字简单粗暴，它存在的意义，就是快，而且效率高！处理大数据最快的排序算法之一。虽然 Worst Case 的时间复杂度达到了 O(n²)，
 *  但是就是优秀，在大多数情况下都比平均时间复杂度为 O(n log n) 的排序算法表现要更好，为什么呢，在《算法艺术与信息学竞赛》的答案：</p>
 *  <p>快速排序的最坏运行情况是 O(n²)，比如说顺序数列的快排。但它的平摊期望时间是 O(n log n)，且 O(n log n) 记号中隐含的常数因子很小，
 * 比复杂度稳定等于 O(n log n) 的归并排序要小很多。所以，对绝大多数顺序性较弱的随机数列而言，快速排序总是优于归并排序</p>
 * @author wsz
 * @version v1.0
 * @date 2020-08-13
 */
public class QuickSort extends AbstractArraySortSimple{
    /**
     * 排序接口
     * <pre>
     *     算法步骤
     *     1.从数列中挑出一个元素，称为 “基准”（pivot）;
     *     2.重新排序数列，所有元素比基准值小的摆放在基准前面，所有元素比基准值大的摆在基准的后面（相同的数可以到任一边）。
     *     在这个分区退出之后，该基准就处于数列的中间位置。这个称为分区（partition）操作；
     *     3.递归地（recursive）把小于基准值元素的子数列和大于基准值元素的子数列排序；
     *  递归的最底部情形，是数列的大小是零或一，也就是永远都已经被排序好了。
     *  虽然一直递归下去，但是这个算法总会退出，因为在每次的迭代（iteration）中，它至少会把一个元素摆到它最后的位置去
     * </pre>
     * @param sourceArray 待排序数组
     * @param order       排序方式，(默认)升序 true；降序 false
     * @return 排序后的数组
     * @throws Exception 异常信息
     */
    @Override
    public Integer[] sort(Integer[] sourceArray, Boolean order) throws Exception {
        Integer[] arr = copyArray(sourceArray);
        start();
        Integer[] res = order ? quickSort(arr, 0, arr.length-1) : quickSortOther(arr,0,arr.length-1);
        end();
        return res;
    }
    private Integer[] quickSort(Integer[] arr,int left,int right){
        if(left < right){
            int pivot = partition(arr, left, right);
            quickSort(arr, left, pivot-1);
            quickSort(arr, pivot+1, right);
        }
        return arr;
    }
    /** 分区操作 */
    private int partition(Integer[] arr,int left,int right){
        //设定基准值（pivot）
        int pivot = arr[left],index = left +1;
        for (int i = index; i <= right ; i++) {
            //升序 arr[i] < pivot；降序 arr[i] > pivot
            if(arr[i] < pivot){
                if(i!=index){swapValBit(arr, i, index);addOne();}
                index ++;
            }
        }
        swapVal(arr, left, index-1);
        addOne();
        return index-1;
    }
    /** 第二种算法实现-降序*/
    private Integer[] quickSortOther(Integer[] arr,int low,int high){
        if(low < high){
            int pivot = partitionOther(arr, low, high);
            quickSortOther(arr, low, pivot - 1);
            quickSortOther(arr, pivot + 1, high);
        }
        return arr;
    }
    /** 第二种算法实现-分区操作 */
    private int partitionOther(Integer[] arr,int low,int high){
        int pivot = arr[low];
        while (low < high){
            //升序 arr[high] > pivot；降序 arr[high] < pivot
            while (low < high && arr[high] < pivot){ --high; }
            arr[low] = arr[high];
            //升序 arr[low] <= pivot；降序 arr[low] >= pivot
            while (low < high && arr[low] >= pivot){ ++low; }
            arr[high] = arr[low];
            addOne();
        }
        arr[low] = pivot;
        return low;
    }
    public static void main(String[] args) {
        int len =5,max=100;
        //待排序数组初始化
        Integer[] arr = randomArray(len,max);
        AbstractArraySortSimple sort = new QuickSort();
        try {
            System.out.println("升序操作");
            printArray(sort.sort(arr, true));
            System.out.println("降序降序");
            printArray(sort.sort(arr, false));
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
